﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using MvcExtensions.Core;
using MvcExtensions.Extensions;

// ReSharper disable once CheckNamespace
namespace MvcExtensions.Controls
{
    public abstract class DynamicTagBase
    {
        public string CssClasses { get; set; }

        public string Attributes { get; set; }

        internal string Data { get; set; }

        internal virtual string ToHtml(string classes = null, string attrributes = null)
        {
            return string.Empty;
        }
    }

    public class DynamicGrid : DynamicTagBase
    {
        public static DynamicTagBuilder DefaultTagBuilder = new DynamicTagBuilder("table");
        private IEnumerable<DynamicGridColumn> _columns;

        #region public members

        public virtual IEnumerable Model { get; set; }

        #endregion

        #region internal members

        internal IEnumerable<DynamicGridColumn> AllColumns
        {
            get { return Columns; }
        }

        internal StringBuilder Text { get; set; }

        #endregion

        public IEnumerable<DynamicGridColumn> Columns
        {
            get { return _columns; }
            set
            {
                _columns = value;
                if (_columns == null) return;
                foreach (var column in _columns)
                {
                    column.Grid = this;
                }
            }
        }

        public DynamicGridFooter Footer { get; set; }

        public DynamicGridHeader Header { get; set; }

        public DynamicGridBody Body { get; set; }

        public virtual MvcHtmlString ToHtml()
        {
            return MvcHtmlString.Empty;
        }
    }

    public class DynamicTagBuilder : DynamicTagBase
    {
        public const string StartTagFormat = "<{0}";
        public const string EndTagFormat = "</{0}>";

        public const string ClassFormat = " class='{0}'";

        const string AsciiTabs = "\t";

        public DynamicTagBuilder(string tag, int noofTable = 1)
        {
            Tag = tag;
            NoofTable = noofTable;
        }

        public int NoofTable { get; set; }

        public string Tag { get; private set; }

        internal string ToHtml(string data = null, string classes = null, string attrributes = null, bool newLineAfterData = true)
        {
            var tagCalsses = (classes ?? CssClasses);
            var tagAttributes = (attrributes ?? Attributes);
            var sbReturn = new StringBuilder(NoofTable +
                (data != null ? data.Length : 0) +
                (tagCalsses != null ? tagCalsses.Length : 0) +
                (tagAttributes != null ? tagAttributes.Length : 0) + 100);

            sbReturn.Append('\n');
            sbReturn.Append(AsciiTabs.PadLeft(NoofTable, '\t'));
            //start tag
            sbReturn.AppendFormat(StartTagFormat, Tag);

            //append classes
            if (!string.IsNullOrEmpty(tagCalsses))
            {
                sbReturn.AppendFormat(ClassFormat, tagCalsses);
            }

            //append any attributes 
            if (!string.IsNullOrEmpty(tagAttributes))
            {
                sbReturn.Append(' ');
                sbReturn.Append(tagAttributes);
            }
            sbReturn.Append('>');
            //append data
            if (data != null)
                sbReturn.Append(data);

            if (newLineAfterData)
            {
                sbReturn.Append('\n');
                sbReturn.Append(AsciiTabs.PadLeft(NoofTable, '\t'));
            }
            //end tag
            sbReturn.AppendFormat(EndTagFormat, Tag);
            return sbReturn.ToString();
        }
    }


    public abstract class DynamicTag : DynamicTagBase
    {
        private readonly DynamicTagBuilder _tagBuilder;

        protected DynamicTag(DynamicTagBuilder tagBuilder)
        {
            _tagBuilder = tagBuilder;
        }

        internal DynamicGrid Grid { get; set; }

        protected internal virtual string GetData(object model = null)
        {
            return string.Empty;
        }

        internal override string ToHtml(string classes = null, string attrributes = null)
        {
            Data = Data ?? GetData();

            return _tagBuilder.ToHtml(Data, (classes ?? CssClasses), (attrributes ?? Attributes));
        }
    }

    public class DynamicGridHeader : DynamicTag
    {
        public static DynamicTagBuilder DefaultTagBuilder = new DynamicTagBuilder("head")
        {
            NoofTable = 2
        };

        public DynamicGridHeader()
            : base(DefaultTagBuilder)
        {
        }

        public DynamicGridRow Row { get; set; }

        protected internal override string GetData(object model = null)
        {
            Row = Row ?? new DynamicGridRow
            {
                Grid = Grid
            };

            var sbHtml = new StringBuilder(Grid.Columns.Sum(x => x.Title.Length));

            foreach (var column in Grid.Columns)
            {
                sbHtml.Append(column.RenderHeader());
            }
            Row.Data = sbHtml.ToString();
            return Row.ToHtml();
        }
    }

    public class DynamicGridBody : DynamicTag
    {
        public static DynamicTagBuilder DefaultTagBuilder = new DynamicTagBuilder("body")
        {
            NoofTable = 2
        };

        public DynamicGridBody()
            : base(DefaultTagBuilder)
        {
        }
        public DynamicGridRow Row { get; set; }

        protected internal override string GetData(object model1 = null)
        {
            Row = Row ?? new DynamicGridRow
            {
                Grid = Grid
            };
            var rowsData = new StringBuilder();
            foreach (var model in Grid.Model)
            {
                var rowData = new StringBuilder();

                foreach (var column in Grid.Columns)
                {
                    rowData.Append(column.ToHtml(model));
                }
                Row.Data = rowData.ToString();

                rowsData.Append(Row.ToHtml());
            }
            return rowsData.ToString();
        }
    }

    public class DynamicGridFooter : DynamicTag
    {
        public static DynamicTagBuilder DefaultTagBuilder = new DynamicTagBuilder("footer")
        {
            NoofTable = 2
        };

        public DynamicGridFooter()
            : base(DefaultTagBuilder)
        {
        }
    }

    public class DynamicGridRow : DynamicTag
    {
        public static DynamicTagBuilder DefaultTagBuilder = new DynamicTagBuilder("tr")
        {
            NoofTable = 3
        };

        public DynamicGridRow()
            : base(DefaultTagBuilder)
        {
        }
    }


    public abstract class DynamicGridColumnBase : DynamicTag
    {
        public static DynamicTagBuilder DefaultHeaderTagBuilder = new DynamicTagBuilder("th")
        {
            NoofTable = 4
        };

        protected DynamicGridColumnBase(string title, DynamicTagBuilder tagBuilder)
            : base(tagBuilder)
        {
            Title = title;
        }

        public string Title { get; protected set; }

        internal string RenderHeader()
        {
            return DefaultHeaderTagBuilder.ToHtml(Title, CssClasses, Attributes, false);
        }
    }

    public class DynamicGridColumn : DynamicGridColumnBase
    {
        public static DynamicTagBuilder DefaultTagBuilder = new DynamicTagBuilder("td")
        {
            NoofTable = 4
        };

        public DynamicGridColumn(string title, string propertyName)
            : base(title, DefaultTagBuilder)
        {
            PropertyName = propertyName;
        }

        public string PropertyName { get; protected set; }

        internal string ToHtml(object model, string classes = null, string attrributes = null)
        {
            Data = GetData(model);

            return DefaultTagBuilder.ToHtml(Data, (classes ?? CssClasses), (attrributes ?? Attributes), false);
        }

        protected internal override string GetData(object model = null)
        {
            var value = ObjectAccessor.GetMetadataValue(PropertyName, model);
            return value != null ? value.ToString() : string.Empty;
        }
    }

    public class DynamicGridColumn<TModel, TValue> : DynamicGridColumn
    {
        private Expression<Func<TModel, TValue>> _expression;

        public DynamicGridColumn(string title, string propertyName)
            : base(title, "")
        {
            PropertyName = propertyName;
        }

        internal Expression<Func<TModel, TValue>> Expression
        {
            get { return _expression; }
            set
            {
                _expression = value;
                PropertyName = ControlExtensions.GetPropertyName(_expression);
            }
        }

        public Func<TModel,string> Format { get; set; }

        protected internal override string GetData(object model = null)
        {
            return Format == null ? base.GetData(model) : Format((TModel)model);
        }
    }

    public class DynamicGridTextColumn<TModel, TValue> : DynamicGridColumn<TModel, TValue>
    {
        readonly DynamicTagBuilder _textTagBuilder = new DynamicTagBuilder("input")
        {
            NoofTable = 5,
            Attributes = "type=\"text\""
        };
        
        public DynamicGridTextColumn(string title, string propertyName) 
            : base(title, propertyName)
        {
        }

        protected internal override string GetData(object model = null)
        {
            var value = Format == null ? base.GetData(model) : Format((TModel)model);

            var attrb = string.Format("{0} value='{1}'", _textTagBuilder.Attributes, value);

            return _textTagBuilder.ToHtml(attrributes: attrb);
        }
    }

    public class DynamicGridCheckboxColumn<TModel, TValue> : DynamicGridColumn<TModel, TValue>
    {
        readonly DynamicTagBuilder _textTagBuilder = new DynamicTagBuilder("input")
        {
            NoofTable = 5,
            Attributes = "type=\"checkbox\""
        };

        public DynamicGridCheckboxColumn(string title, string propertyName)
            : base(title, propertyName)
        {
        }

        protected internal override string GetData(object model = null)
        {
            var value = Format == null ? base.GetData(model) : Format((TModel)model);

            var attrb = string.Format("{0} value='{1}'", _textTagBuilder.Attributes, value);

            return _textTagBuilder.ToHtml(attrributes: attrb);
        }
    }
    public class DynamicGrid<T> : DynamicGrid
    {
        private readonly IEnumerable<T> _model;

        public DynamicGrid(IEnumerable<T> model)
        {
            _model = model;
        }

        public override IEnumerable Model
        {
            get { return _model; }
            set { throw new NotImplementedException(); }
        }

        public HtmlHelper<IEnumerable<T>> HtmlHelper { get; set; }

        public override MvcHtmlString ToHtml()
        {
            Header = Header ?? new DynamicGridHeader();
            Header.Grid = this;

            Body = Body ?? new DynamicGridBody();
            Body.Grid = this;

            Footer = Footer ?? new DynamicGridFooter();
            Footer.Grid = this;

            Data = Header.ToHtml() + Body.ToHtml();

            return MvcHtmlString.Create(DefaultTagBuilder.ToHtml(Data, CssClasses, Attributes));
        }
    }

}
